/*-----------------------------------------------------------------------------------*/
/* Nuvoton Technology Corporation confidential                                       */
/*                                                                                   */
/* Copyright (c) 2008 by Nuvoton Technology Corporation                              */
/* All rights reserved                                                               */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/
/* 
 * Driver for FMI devices
 * SM layer glue code
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "nuc900_reg.h"
#include "fmi.h"
#include "nuc900_sic.h"
#include "nvtfat.h"

DISK_DATA_T MS_DiskInfo;

PDISK_T *pDisk_MS0 = NULL;
PDISK_T *pDisk_MS1 = NULL;
extern INT  fsPhysicalDiskConnected(PDISK_T *pDisk);

FMI_MS_INFO_T *pMS0, *pMS1;

static INT  ms_disk_init(PDISK_T  *lDisk)
{
	return 0;
}


static INT  ms_disk_ioctl(PDISK_T *lDisk, INT control, VOID *param)
{
	return 0;
}

static INT  ms_disk_read(PDISK_T *pDisk, UINT32 sector_no, INT number_of_sector, UINT8 *buff)
{
	int status;

	// enable MS
	outpw(REG_FMICSR, 0x04);

	if (pDisk == pDisk_MS0)
	{
		outpw(REG_MSCSR, inpw(REG_MSCSR) & 0xffdfffff);
		status = fmiMS_Read_in(pMS0, sector_no, number_of_sector, (char *)buff);
	}
	else if (pDisk == pDisk_MS1)
	{
		outpw(REG_MSCSR, inpw(REG_MSCSR) | 0x00200000);
		status = fmiMS_Read_in(pMS1, sector_no, number_of_sector, (char *)buff);
	}

	if (status != Successful)
		return status;

	return FS_OK;
}


static INT  ms_disk_write(PDISK_T *pDisk, UINT32 sector_no, INT number_of_sector, UINT8 *buff, BOOL IsWait)
{
	int status;

	// enable MS
	outpw(REG_FMICSR, 0x04);

	if (pDisk == pDisk_MS0)
	{
		outpw(REG_MSCSR, inpw(REG_MSCSR) & 0xffdfffff);
		status = fmiMS_Write_in(pMS0, sector_no, number_of_sector, (char *)buff);
	}
	else if (pDisk == pDisk_MS1)
	{
		outpw(REG_MSCSR, inpw(REG_MSCSR) | 0x00200000);
		status = fmiMS_Write_in(pMS1, sector_no, number_of_sector, (char *)buff);
	}

	if (status != Successful)
		return status;

	return FS_OK;
}



STORAGE_DRIVER_T  _MSDiskDriver = 
{
	ms_disk_init,
	ms_disk_read,
	ms_disk_write,
	ms_disk_ioctl,
};


INT fmiInitMSDevice(UINT32 uChipSel)	/* int sd_init_onedisk(INT i) */
{
	PDISK_T *pDisk;
	int volatile rate, i;

	/* MS enabled */
	outpw(REG_CLKEN1, inpw(REG_CLKEN1) | 0x01);
	outpw(REG_FMICSR, 0x04);
	outpw(REG_MSCSR, 0x00000008);
	outpw(REG_MSCSR, inpw(REG_MSCSR) | 0x01);
	while(inpw(REG_MSCSR) & 0x01);

//#ifndef _FPGA_
	/* divider */
	if (_fmi_uFMIReferenceClock >= 133000)
	{
		outpw(REG_CLKSEL, inpw(REG_CLKSEL) | 0x1000);
		_fmi_uFMIReferenceClock = _fmi_uFMIReferenceClock >> 1;
	}

	rate = _fmi_uFMIReferenceClock / 20000;
	if ((_fmi_uFMIReferenceClock % 20000) == 0)
		rate = rate - 1;
	outpw(REG_CLKDIV1, (inpw(REG_CLKDIV1) & 0xff00) | rate);		// bit0 ~ 7
//#endif

	if (uChipSel == 0)
	{
		// enable MS0 power
		outpw(0xB000000C, (inpw(0xB000000C) & 0xffffff0f)|0xf0);
		outpw(0xb8003014, 0x100);	// set gpiod-8 to output
		outpw(0xb8003018, inpw(0xb8003018) | 0x100);	// set gpiod-8 output high
		for (i=0; i<10000; i++);
		outpw(0xb8003018, inpw(0xb8003018) & 0xfffffeff);	// set gpiod-8 output low

		pMS0 = malloc(sizeof(FMI_MS_INFO_T));
		memset((char *)pMS0, 0, sizeof(FMI_MS_INFO_T));

		outpw(REG_MSCSR, inpw(REG_MSCSR) & 0xffdfffff);

		// check card
		outpw(REG_MSISR, 0xffffffff);
		outpw(REG_MSIER, inpw(REG_MSIER)|0x10000);
		if (inpw(REG_MSISR) & 0x1000000)
		{
			pMS0->bIsCardInsert = FALSE;
			return FMI_NO_MS_CARD;
		}
		else
			pMS0->bIsCardInsert = TRUE;

		if ((i=fmiMS_Initial(pMS0)) < 0)
			return i;
			//return FMI_MS_INIT_ERROR;
#if 0
//#ifndef _FPGA_
		if (pMS0->bIsMSProParallel == TRUE)
		{
			/* divider */
			rate = _fmi_uFMIReferenceClock / 40000;
			if ((_fmi_uFMIReferenceClock % 40000) == 0)
				rate = rate - 1;
			outpw(REG_CLKDIV1, (inpw(REG_CLKDIV1) & 0xff00) | rate);		// bit0 ~ 7
		}
#endif
		/* init MS interface */
		fmiGet_MS_info(pMS0, &MS_DiskInfo);
	}
	else if (uChipSel == 1)
	{
		// enable MS1 power
		outpw(0xB000000C, (inpw(0xB000000C) & 0xffe3ffff)|0x1c0001);
		outpw(0xb8003044, 0x20);	// set gpiog-5 to output
		outpw(0xb8003048, inpw(0xb8003048) | 0x20);	// set gpiog-5 output high
		for (i=0; i<10000; i++);
		outpw(0xb8003048, inpw(0xb8003048) & 0xffffffdf);	// set gpiog-5 output low

		pMS1 = malloc(sizeof(FMI_MS_INFO_T));
		memset((char *)pMS1, 0, sizeof(FMI_MS_INFO_T));

		outpw(REG_MSCSR, inpw(REG_MSCSR) | 0x00200000);

		// check card
		outpw(REG_MSISR, 0xffffffff);
		outpw(REG_MSIER, inpw(REG_MSIER)|0x20000);
		if (inpw(REG_MSISR) & 0x2000000)
		{
			pMS1->bIsCardInsert = FALSE;
			return FMI_NO_MS_CARD;
		}
		else
			pMS1->bIsCardInsert = TRUE;

		if ((i=fmiMS_Initial(pMS1)) < 0)
			return i;
			//return FMI_MS_INIT_ERROR;

#if 0
//#ifndef _FPGA_
		if (pMS1->bIsMSProParallel == TRUE)
		{
			/* divider */
			rate = _fmi_uFMIReferenceClock / 40000;
			if ((_fmi_uFMIReferenceClock % 40000) == 0)
				rate = rate - 1;
			outpw(REG_CLKDIV1, (inpw(REG_CLKDIV1) & 0xff00) | rate);		// bit0 ~ 7
		}
#endif
		/* init MS interface */
		fmiGet_MS_info(pMS1, &MS_DiskInfo);
	}

	/* 
	 * Create physical disk descriptor 
	 */
	pDisk = malloc(sizeof(PDISK_T));
	memset((char *)pDisk, 0, sizeof(PDISK_T));

	/* read Disk information */
	pDisk->szManufacture[0] = '\0';
	strcpy(pDisk->szProduct, (char *)MS_DiskInfo.product);
	strcpy(pDisk->szSerialNo, (char *)MS_DiskInfo.serial);

	pDisk->nDiskType = DISK_TYPE_SD_MMC;
	pDisk->nPartitionN = 0;
	pDisk->ptPartList = NULL;
	
	pDisk->nSectorSize = 512;
	pDisk->uTotalSectorN = MS_DiskInfo.totalSectorN;
	pDisk->uDiskSize = MS_DiskInfo.diskSize;

	/* create relationship between UMAS device and file system hard disk device */
	
	pDisk->ptDriver = &_MSDiskDriver;

#ifdef DEBUG
	printf("MS disk found: size=%d MB\n", (int)pDisk->uDiskSize / 1024);
#endif

	if (uChipSel == 0)
		pDisk_MS0 = pDisk;
	else
		pDisk_MS1 = pDisk;

	fsPhysicalDiskConnected(pDisk);

	return pDisk->uTotalSectorN;

} 	/* end InitIDEDevice */

INT  fmiMS_Read(UINT32 uChipSel, UINT32 uSector, UINT32 uBufcnt, UINT32 uDAddr)
{
	int status=0;

	// enable MS
	outpw(REG_FMICSR, 0x04);

	if (uChipSel == 0)
	{
		outpw(REG_MSCSR, inpw(REG_MSCSR) & 0xffdfffff);
		status = fmiMS_Read_in(pMS0, uSector, uBufcnt, (char *)uDAddr);
	}
	else if (uChipSel == 1)
	{
		outpw(REG_MSCSR, inpw(REG_MSCSR) | 0x00200000);
		status = fmiMS_Read_in(pMS1, uSector, uBufcnt, (char *)uDAddr);
	}
	return status;
}

INT  fmiMS_Write(UINT32 uChipSel, UINT32 uSector, UINT32 uBufcnt, UINT32 uSAddr)
{
	int status=0;

	// enable MS
	outpw(REG_FMICSR, 0x04);

	if (uChipSel == 0)
	{
		outpw(REG_MSCSR, inpw(REG_MSCSR) & 0xffdfffff);
		status = fmiMS_Write_in(pMS0, uSector, uBufcnt, (char *)uSAddr);
	}
	else if (uChipSel == 1)
	{
		outpw(REG_MSCSR, inpw(REG_MSCSR) | 0x00200000);
		status = fmiMS_Write_in(pMS1, uSector, uBufcnt, (char *)uSAddr);
	}
	return status;
}


